﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using Thor.Domain;
using Thor.Domain.Configuration;

namespace Thor.Domain
{
    public class InvalidLogonMonitor
    {
                private InvalidLogonMonitorParameters _parameters = null;
        private bool _isRunning = false;
        private bool _isPaused = false;
        private Timer _timer = null;

        /// <summary>
        /// Starting event.
        /// </summary>
        public event EventHandler Starting;

        /// <summary>
        /// Started event.
        /// </summary>
        public event EventHandler Started;

        /// <summary>
        /// Stopping event.
        /// </summary>
        public event EventHandler Stopping;

        /// <summary>
        /// Stopped event.
        /// </summary>
        public event EventHandler Stopped;

        /// <summary>
        /// Pausing event.
        /// </summary>
        public event EventHandler Pausing;

        /// <summary>
        /// Paused event.
        /// </summary>
        public event EventHandler Paused;

        /// <summary>
        /// Resuming event.
        /// </summary>
        public event EventHandler Resuming;

        /// <summary>
        /// Resumed event.
        /// </summary>
        public event EventHandler Resumed;

        public InvalidLogonMonitor(InvalidLogonMonitorParameters parameters)
        {
            if (parameters == null)
                throw new Exception("Parameters cannot be null.");

            parameters.Validate();

            _parameters = parameters;
        }

        /// <summary>
        /// Indicates whether or not the monitor is running.
        /// </summary>
        public bool IsRunning
        {
            get { return _isRunning; }
            private set { _isRunning = value; }
        }

        /// <summary>
        /// Starts monitoring.
        /// </summary>
        public void Start()
        {
            if (!_isRunning && !_isPaused)
            {
                OnStarting();

                double secondsInterval = _parameters.Interval * 60000;

                if (_parameters.LoadOnStart)
                    UpdateInvalidLogons();

                _timer = new Timer(secondsInterval);
                _timer.Elapsed += new ElapsedEventHandler(Timer_Elapsed);
                _timer.Start();

                _isRunning = true;

                OnStarted();
            }
        }

        /// <summary>
        /// Stops monitoring.
        /// </summary>
        public void Stop()
        {
            if (_isRunning || _isPaused)
            {
                OnStopping();

                _timer.Stop();
                _timer.Dispose();
                _timer = null;

                _isRunning = false;

                OnStopped();
            }
        }

        /// <summary>
        /// Pauses monitoring.
        /// </summary>
        public void Pause()
        {
            if (_isRunning && !_isPaused)
            {
                OnPausing();

                _timer.Stop();
                _isRunning = false;
                _isPaused = true;

                OnPaused();
            }
        }

        /// <summary>
        /// Resumes monitoring.
        /// </summary>
        public void Resume()
        {
            if (!_isRunning && _isPaused)
            {
                OnResuming();

                _timer.Start();
                _isRunning = true;
                _isPaused = false;

                OnResumed();
            }
        }

        private void UpdateInvalidLogons()
        {
            var invalidLogons = _parameters.Repository.Fetch();

            if (invalidLogons != null)
            {
                var retryCount = _parameters.Settings.RetryCount;
                var lockoutDuration = _parameters.Settings.LockoutDuration;

                if (retryCount > 0 && lockoutDuration > 0)
                {
                    foreach (var invalidLogon in invalidLogons)
                    {
                        if (invalidLogon.AttemptCount >= retryCount)
                        {
                            DateTime lockOutEnd = invalidLogon.LastAttemptTimestamp.AddMinutes(lockoutDuration);

                            if (DateTime.UtcNow >= lockOutEnd)
                                _parameters.Repository.Delete(invalidLogon);
                        }
                        else if (invalidLogon.LastAttemptTimestamp.AddMinutes(_parameters.Settings.RetryInterval) >= DateTime.UtcNow)
                        {
                            _parameters.Repository.Delete(invalidLogon);
                        }
                    }
                }
                else
                {
                    foreach (var invalidLogon in invalidLogons)
                        _parameters.Repository.Delete(invalidLogon);
                }
            }
        }

        /// <summary>
        /// Updates the calendar.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            _timer.Stop();

            UpdateInvalidLogons();

            _timer.Start();
        }

        /// <summary>
        /// Invokes the starting event.
        /// </summary>
        private void OnStarting()
        {
            if (Starting != null)
                Starting(this, new EventArgs());
        }

        /// <summary>
        /// Invokes the started event.
        /// </summary>
        private void OnStarted()
        {
            if (Started != null)
                Started(this, new EventArgs());
        }

        /// <summary>
        /// Invokes the stopping event.
        /// </summary>
        private void OnStopping()
        {
            if(Stopping != null)
                Stopping(this, new EventArgs());
        }

        /// <summary>
        /// Invokes the stopped event.
        /// </summary>
        private void OnStopped()
        {
            if(Stopped != null)
                Stopped(this, new EventArgs());
        }

        /// <summary>
        /// Invokes the pausing event.
        /// </summary>
        private void OnPausing()
        {
            if(Pausing != null)
                Pausing(this, new EventArgs());
        }

        /// <summary>
        /// Invokes the paused event.
        /// </summary>
        private void OnPaused()
        {
            if(Paused != null)
                Paused(this, new EventArgs());
        }

        /// <summary>
        /// Invokes the resuming event.
        /// </summary>
        private void OnResuming()
        {
            if(Resuming!= null)
                Resuming(this, new EventArgs());
        }

        /// <summary>
        /// Invokes the resumed event.
        /// </summary>
        private void OnResumed()
        {
            if(Resumed != null)
                Resumed(this, new EventArgs());
        }
    }

    public enum InvalidLogonMonitorStatus
    {
        Running,
        Stopped,
        Paused
    }

    public class InvalidLogonMonitorParameters
    {
        public IInvalidLogonRepository Repository { get; set; }
        public Settings Settings { get; set; }
        public int Interval { get; set; }
        public bool LoadOnStart { get; set; }

        public void Validate()
        {
            if (Repository == null)
                throw new Exception("InvalidLogonrMonitorParameters.Repository cannot be null.");

            if(Settings == null)
                throw new Exception("InvalidLogonrMonitorParameters.Settings cannot be null.");

            if (Interval < 1 || Interval > 1440)
                throw new Exception("InvalidLogonrMonitorParameters.Interval must be greater than 1 and less than 1440.");
        }
    }
}
